//
//  Model3DView.m
//  iDent
//
//  Created by Marius Corega on 7/21/09.
//  Copyright 2009 Marius Corega. All rights reserved.
//

#import "Model3DView.h"
#import "STLElement.h"
#import <OpenGL/OpenGL.h>

//#import "gl.h"
/*
typedef unsigned int GLuint;

#define GL_LIGHTING                       0x0B50
#define GL_COMPILE_AND_EXECUTE            0x1301
#define GL_TRIANGLES                      0x0004
#define GL_LINES                          0x0001
#define GL_MODELVIEW                      0x1700
#define GL_PROJECTION                     0x1701
#define GL_TEXTURE                        0x1702
#define GL_COLOR_BUFFER_BIT               0x00004000
#define GL_DEPTH_BUFFER_BIT               0x00000100
#define GL_LIGHT0                         0x4000
#define GL_LIGHT1                         0x4001
#define GL_POSITION                       0x1203
#define GL_LIGHT_MODEL_AMBIENT            0x0B53
#define GL_LEQUAL                         0x0203
#define GL_DEPTH_TEST                     0x0B71
#define GL_COLOR_MATERIAL                 0x0B57
#define GL_SRC_ALPHA                      0x0302
#define GL_ONE_MINUS_SRC_ALPHA            0x0303
#define GL_BLEND                          0x0BE2


typedef float GLfloat;
*/

@implementation Model3DView

@synthesize showXAxes;

- (id) initWithFrame: (NSRect) frame
{
	
	GLuint attribs[] = 
	{
	//	NSOpenGLPFANoRecovery,
		NSOpenGLPFAWindow,
		NSOpenGLPFAAccelerated,
		NSOpenGLPFADoubleBuffer,
		NSOpenGLPFAColorSize, 24,
		NSOpenGLPFAAlphaSize, 8,
		NSOpenGLPFADepthSize, 24,
		NSOpenGLPFAStencilSize, 8,
		NSOpenGLPFAAccumSize, 0,
		0
	};
	
	NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; 
	
	if (!fmt)
		NSLog(@"No OpenGL pixel format");
	
	
	return self = [super initWithFrame:frame pixelFormat: [fmt autorelease]];
	
}


- (void) awakeFromNib
{
	/*
	color_index = alphaIndex;
	
	myDent = new CSimDent3D();
	NSString *cale = [[NSBundle mainBundle] resourcePath];
	cale = [cale stringByAppendingString: @"/tooth.s3d"];
	char *pp = (char*)[cale UTF8String];
	myDent->myArcada.LoadDataFile(pp);
	showAxes = false;
	 */
	//myDent->InitGL();
	zoomValue = -90.0f;
	modelDL = 0;
	transparency = 1.0f;
	
	[dintePB removeAllItems];
	
	for (int i=1; i<=4; i++) {
		for (int j=1; j<=8; j++)
		{
			float d = i + (float) j/10;
			NSNumber *dinte = [NSNumber numberWithFloat:d];
			
			[dintePB addItemWithTitle:[dinte stringValue]];
		}
	}
	
	modelDict = [NSMutableDictionary dictionary];
	[modelDict retain];
	
	showXAxes = YES;
	showYAxes = YES;
	showZAxes = YES;
	
}

/*
 Override the view's drawRect: to draw our GL content.
 */	 

-(void) drawAxes
{
	glDisable(GL_LIGHTING);
	glBegin(GL_LINES);
	float inc = 1.0f;
	float dim = 30.0f;
	
	if(showXAxes)
	for (float i=-dim;i<=dim ; i+=inc) {
		glColor3f(2.0, 0.0, 0.0);
		glVertex3f(-dim,  0.0, i);
		glVertex3f(dim,  0.0, i);		
		

		
		glColor3f(2.0, 0.0, 0.0);
		glVertex3f(i,  0.0, -dim);
		glVertex3f(i,  0.0, dim);		
		
		if((int)i % 10 == 0) 
		{
			glVertex3f(-dim,  0.0, i+0.1);
			glVertex3f(dim,  0.0, i+0.1);					

			glVertex3f(i+0.1,  0.0, -dim);
			glVertex3f(i+0.1,  0.0, dim);		

		}
		
		
	}
	
	glColor3f(0.0, 2.0, 0.0);
	if(showYAxes)
	for (float i=-dim;i<=dim ; i+=inc) {	
		glVertex3f(i, -dim,  0.0);
		glVertex3f(i, dim,  0.0);
	}
	
	glColor3f(0.0, 0.0, 2.0);
	if(showZAxes)
	for (float i=-dim;i<=dim ; i+=inc) {	
		glVertex3f(0.0, i, dim);
		glVertex3f(0.0, i, -dim);
	}
	glEnd();	
	glEnable(GL_LIGHTING);
	
}

- (void) drawRect: (NSRect) rect
{
	/*
	 glViewport(0, 0, (GLsizei) rect.size.width, (GLsizei) rect.size.height);
	 
	 GLfloat clear_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
	 clear_color[color_index] = 1.0f;
	 
	 glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]);
	 
	 glClear(GL_COLOR_BUFFER_BIT+GL_DEPTH_BUFFER_BIT+GL_STENCIL_BUFFER_BIT);
	 
	 myDent->Display();
	 
	 [[self openGLContext] flushBuffer];
	 
	 */
	
	if(color == nil) color = [NSColor colorWithCalibratedRed:(float)0xccc / 0xff green:(float)0x99 / 0xff  blue:(float)0x33 / 0xff alpha:1.0];
	[color retain];
	
	NSRect bounds = [self bounds];
	double zoom = 1.0;
	
	float aspect = NSWidth(bounds) / NSHeight(bounds);
	
	
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	{
		float lr, bt;
		
		if (aspect > 1)
		{
			lr = 0.3 * aspect * zoom;
			bt = 0.3 * zoom;
		}
		else
		{
			lr = 0.3 * zoom;
			bt = 0.3 / aspect * zoom;
		}
		
		glFrustum(-lr, lr, -bt, bt, 1.0, 200.0);
	}
	glMatrixMode(GL_MODELVIEW);
	
	glViewport(0, 0, NSWidth(bounds), NSHeight(bounds));
	
	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	
	glLoadIdentity();
	
	glEnable(GL_LIGHTING);
	
	GLfloat light_position[] = {0.0, -50.1, -10.1, 1.0};  /* Infinite light location. */
	
	//	glLightfv(GL_LIGHT0, GL_AMBIENT, light_diffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	glEnable(GL_LIGHT0);
//	glEnable(GL_LIGHT1);
	glEnable(GL_LIGHTING);
	//glEnable(GL_COLOR_MATERIAL);
	
	GLfloat light_diffuse[] = {1.0, 0.1, 0.1, 0.1};  /* Red diffuse light. */
	
	GLfloat light_position1[] = {0.0, 0.0, 50.0, 0.0};  /* Infinite light location. */
	GLfloat f = 0.3f;
	GLfloat light_mode[] = {f, f, f, 1.0}; 
	
	glEnable(GL_LIGHTING);
	
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	//glLoadIdentity();
	//	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	//	glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position); 
	
	glLightModelfv(	GL_LIGHT_MODEL_AMBIENT, light_mode);
	
	
	/* Use depth buffering for hidden surface elimination. */
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);

	
	glTranslatef(0.0, 0.0, zoomValue);
	
	
	glRotatef(rotAngle.x, 0.0f, 1.0f, 0.0f);
	glRotatef(rotAngle.y, 1.0f, 0.0f, 0.0f);
	//	glRotatef(rotAngle.z, 0, 0, 1);
	
	
	
	
	if(modelDL > 0)
	{
		if(color != nil) glColor4f([color redComponent], [color greenComponent], [color blueComponent], transparency);
		
		if([transparentModelBut state] == NSOnState)
		{
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
		glDisable(GL_DEPTH_TEST);
		}
		else
		{
			glDisable(GL_BLEND);
			glEnable(GL_DEPTH_TEST);
		}
		
		glPushMatrix();
		glTranslatef(transPoint.x, transPoint.y, transPoint.z);
		glRotatef(modelRotAngle.x, 0.0f, 1.0f, 0.0f);
		glRotatef(modelRotAngle.y, 1.0f, 0.0f, 0.0f);
		glCallList(modelDL);
		glPopMatrix();
	}
	
	glDisable(GL_BLEND);

	if([showAxesBut state] == NSOnState)[self drawAxes];
	
	[[self openGLContext] flushBuffer];
	
	
	
}

- (void)mouseDown:(NSEvent *)event
{
	NSPoint lastMousePoint = [self convertPoint:[event locationInWindow] fromView:nil];
	
	initPoint = lastMousePoint;
}

- (void)rightMouseDown:(NSEvent *)event
{
	NSPoint lastMousePoint = [self convertPoint:[event locationInWindow] fromView:nil];
	//	rightMouseIsDown = YES;
	
}

- (void)mouseUp:(NSEvent *)event
{

}

- (void)rightMouseUp:(NSEvent *)event
{

}

- (void)mouseDragged:(NSEvent *)event
{
	NSPoint lastMousePoint = [self convertPoint:[event locationInWindow] fromView:nil];
	NSInteger moveScale = 2;
	
	if([event modifierFlags] & NSShiftKeyMask)
	{
		transPoint.x += (lastMousePoint.x - initPoint.x) / moveScale;
		transPoint.z -= (lastMousePoint.y - initPoint.y) / moveScale;
		
	}
	else if([event modifierFlags] & NSControlKeyMask)
	{
		modelRotAngle.x += (lastMousePoint.x - initPoint.x) / moveScale;
		modelRotAngle.y -= (lastMousePoint.y - initPoint.y) / moveScale;
		
	}
	else		
	{

	rotAngle.x += (lastMousePoint.x - initPoint.x) / moveScale;
	rotAngle.y -= (lastMousePoint.y - initPoint.y) / moveScale;
	}
		
	initPoint = lastMousePoint;
	[self setNeedsDisplay:YES];
	
}

- (void)rightMouseDragged:(NSEvent *)event
{
	NSPoint lastMousePoint = [self convertPoint:[event locationInWindow] fromView:nil];
	


	
	[self setNeedsDisplay:YES];
}

- (void)scrollWheel:(NSEvent *)theEvent
{
	NSNumber *z = [NSNumber numberWithFloat: zoomValue + [theEvent deltaY]/10];
	if(zoomValue <0){zoomValue = [z floatValue]; }
	[self setNeedsDisplay:YES];
}

-(NSArray*) trianglesBetweenMaxVector:(Vector3D) max andMinVector:(Vector3D) min
{
	NSMutableArray *tri = [[NSMutableArray alloc] init];
	unsigned long count = [trianglesArray count];
	
	for(unsigned long i=0; i < count; i++)
	{
		STLElement *e = [trianglesArray objectAtIndex:i];
		TSTLElement el = [e element];
		
		if((el.v1.x < max.x)&&(el.v2.x < max.x)&&(el.v3.x < max.x)&&
		   (el.v1.y < max.y)&&(el.v2.y < max.y)&&(el.v3.y < max.y)&&
		   (el.v1.z < max.z)&&(el.v2.z < max.z)&&(el.v3.z < max.z)&&
		   (el.v1.x > min.x)&&(el.v2.x > min.x)&&(el.v3.x > min.x)&&
		   (el.v1.y > min.y)&&(el.v2.y > min.y)&&(el.v3.y > min.y)&&
		   (el.v1.z > min.z)&&(el.v2.z > min.z)&&(el.v3.z > min.z))
		{
			[tri addObject:e];
		}		
	}
	
	return tri;
}

-(void) generateDisplayLists
{
	Vector3D max, min;
	 max.x = [xMaxSlider floatValue]; min.x = [xMinSlider floatValue];
	 max.y = [yMaxSlider floatValue]; min.y = [yMinSlider floatValue];
	 max.z = [zMaxSlider floatValue]; min.z = [zMinSlider floatValue];
	
	
	if (modelDL != 0) {glDeleteLists(modelDL, 1); modelDL = 0;}
	
	if(modelDL==0)
	{
		modelDL = glGenLists(1);
		glNewList(modelDL, GL_COMPILE_AND_EXECUTE);
		glBegin(GL_TRIANGLES);
		unsigned long count = [trianglesArray count];
		for(unsigned long i=0; i < count; i++)
		{
			STLElement *e = [trianglesArray objectAtIndex:i];
			TSTLElement el = [e element];
			
			if((el.v1.x < max.x)&&(el.v2.x < max.x)&&(el.v3.x < max.x)&&
			   (el.v1.y < max.y)&&(el.v2.y < max.y)&&(el.v3.y < max.y)&&
			   (el.v1.z < max.z)&&(el.v2.z < max.z)&&(el.v3.z < max.z)&&
			   (el.v1.x > min.x)&&(el.v2.x > min.x)&&(el.v3.x > min.x)&&
			   (el.v1.y > min.y)&&(el.v2.y > min.y)&&(el.v3.y > min.y)&&
			   (el.v1.z > min.z)&&(el.v2.z > min.z)&&(el.v3.z > min.z))
			{
				
				glNormal3f(el.normal.x, el.normal.y, el.normal.z);
				
				glVertex3f(el.v1.x, el.v1.y, el.v1.z);
				glVertex3f(el.v2.x, el.v2.y, el.v2.z);
				glVertex3f(el.v3.x, el.v3.y, el.v3.z);
				
			}

		}
		glEnd();
		glEndList();
	}
	
}

/*
 The UI buttons are targetted to call this action method:
 */

-(IBAction) loadModel3D:(id)sender
{
//	NSData *data= [NSData dataWithContentsOfFile:@"/Users/cipi/1.stl"];
	//NSData *data= [NSData dataWithContentsOfFile:@"/modele3D/1.stl"];

//	STLReader *reader = [[STLReader alloc] initWithData:data];
	STLReader *reader = [STLReader stlReaderFromFile:@"/modele3D/1.stl"];
//	[reader retain];
	
	
	if (reader != nil)
	{
		if(trianglesArray != nil) [trianglesArray release];
		trianglesArray = [[reader vertex] copy];
		
		[self generateDisplayLists];
		
		[reader release];
		reader = nil;
	}
	
	[self setNeedsDisplay:YES];
}

-(IBAction) saveModel3D:(id)sender
{
	[modelDict writeToFile:@"/modele3D/model.i3d" atomically:NO];
}

-(IBAction) saveModelDinte3D:(id)sender
{
	NSString *dinte = [[dintePB selectedItem] title];
	Vector3D max, min;
	max.x = [xMaxSlider floatValue]; min.x = [xMinSlider floatValue];
	max.y = [yMaxSlider floatValue]; min.y = [yMinSlider floatValue];
	max.z = [zMaxSlider floatValue]; min.z = [zMinSlider floatValue];
	
	NSArray *tri = [self trianglesBetweenMaxVector:max andMinVector:min];
	NSMutableData *endTri = [NSMutableData data];
	
	for(int i = 0; i< [tri count]; i++) [endTri appendData:[[tri objectAtIndex:i] dataValue]];
	
	[modelDict setValue:endTri forKey:dinte];
}

-(IBAction) resetModelDinte3D:(id)sender
{
	[xMinSlider setFloatValue:-40.0f ];
}

- (IBAction) setModelColor: (id) sender
{
	[color release];
	color = (NSColor*)[sender color];
	[color retain];
	
	[self setNeedsDisplay: YES];
}

-(IBAction) showAxes:(id) sender
{
	[self setNeedsDisplay:YES];
}

-(IBAction) changeTransparency:(id) sender
{
	transparency = [sender floatValue];
	
	[self setNeedsDisplay:YES];
}

-(IBAction) changeLimit:(id) sender
{
	//yLimit = [sender floatValue];
	
	[self generateDisplayLists];
	[self setNeedsDisplay:YES];
}

-(IBAction) showToolWindow:(id) sender
{
	if([toolWindow isVisible]) [toolWindow orderOut:self];
	else [toolWindow orderFront:self];
	
}

-(IBAction) showXAxes:(id) sender
{
	showXAxes = !showXAxes;
	[self setNeedsDisplay:YES];
}

-(IBAction) showYAxes:(id) sender
{
	showYAxes = !showYAxes;
	[self setNeedsDisplay:YES];

}

-(IBAction) showZAxes:(id) sender
{
	showZAxes = !showZAxes;
	[self setNeedsDisplay:YES];
	
}


@end
